package de.fhluebeck.oop.gps.map;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.util.HashSet;
import java.util.Set;

import javax.swing.ImageIcon;

import org.jdesktop.swingx.JXMapKit;
import org.jdesktop.swingx.JXMapViewer;
import org.jdesktop.swingx.mapviewer.DefaultTileFactory;
import org.jdesktop.swingx.mapviewer.GeoPosition;
import org.jdesktop.swingx.mapviewer.TileFactory;
import org.jdesktop.swingx.mapviewer.TileFactoryInfo;
import org.jdesktop.swingx.mapviewer.Waypoint;
import org.jdesktop.swingx.mapviewer.WaypointPainter;
import org.jdesktop.swingx.mapviewer.WaypointRenderer;
import org.jdesktop.swingx.mapviewer.util.GeoUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.fhluebeck.oop.gps.common.Constants;
import de.fhluebeck.oop.gps.common.PoiEnabledWaypoint;
import de.fhluebeck.oop.gps.entity.Poi;

/**
 * Diese Klasse dient dazu Algorithmen zu Bestimmung von Koordinaten, 
 * und die Umwandlung von Gerätekoordinaten in Modelkoordinaten zu überführen.
 * Sie abstrahiert sämtliche Zugriffe auf die Map und Operation auf der Map 
 * notwendig sind.
 * 
 * @author Ogün Bilge, Leif Hitzschke
 * @version 1.00, 05.11.2009
 * 
 */
public class MapManager {

	private static final int ICON_SIZE = 32;
	private final static Logger log = LoggerFactory.getLogger(MapManager.class);

	private final JXMapKit mapKit;
	private final JXMapViewer map;

	private WaypointPainter<JXMapViewer> painter;
	private Set<Waypoint> waypoints;
	private WaypointUpdateListener waypointUpdateListener;
	private WaypointCreateListener waypointCreateListener;

	public MapManager(JXMapKit mapKit) {
		this.mapKit = mapKit;
		map = mapKit.getMainMap();
	}

	public void setWaypointUpdateListener(WaypointUpdateListener waypointUpdateListener) {
		this.waypointUpdateListener = waypointUpdateListener;
	}

	public void setWaypointCreateListener(
			WaypointCreateListener waypointCreateListener) {
		this.waypointCreateListener = waypointCreateListener;
	}

	public void setUp() {

		final TileFactory tf = new DefaultTileFactory(new GPSTileFactoryInfo());
		mapKit.setTileFactory(tf);

		GeoPosition center = new GeoPosition(Constants.CENTER_LAT,
				Constants.CENTER_LNG);

		mapKit.setCenterPosition(center);
		mapKit.setDataProviderCreditShown(false);
		painter = new WaypointPainter<JXMapViewer>();
		painter.setRenderer(new WaypointRenderer() {

			@Override
			public boolean paintWaypoint(Graphics2D g, JXMapViewer map,
					Waypoint wp) {

				Image marker = null;
				if (wp instanceof PoiEnabledWaypoint) {
					ImageIcon icon = new ImageIcon(
							MapManager.class
									.getResource("/de/fhluebeck/oop/gps/resources/map-green.png"));
					marker = icon.getImage();

				} else {
					ImageIcon icon = new ImageIcon(
							MapManager.class
									.getResource("/de/fhluebeck/oop/gps/resources/map-orange.png"));
					marker = icon.getImage();
				}

				g.drawImage(marker, -5, -ICON_SIZE, null);
				return true;
			}
		});
		waypoints = new HashSet<Waypoint>();

		map.setOverlayPainter(painter);

		map.addMouseListener(new MouseAdapter() {

			private void wayPointClicked(MouseEvent event) {
				// Get the pixel coordinates of the waypoint in question from
				// the map.
				for (Waypoint waypoint : waypoints) {
					if (waypoint instanceof PoiEnabledWaypoint) {
						Point2D point = tf.geoToPixel(waypoint.getPosition(),
								map.getZoom());

						// Adjust the pixel coordinates to their relative
						// position on screen.
						Rectangle bounds = map.getViewportBounds();
						int x = (int) (point.getX() - bounds.getX());
						int y = (int) (point.getY() - bounds.getY());

						// Create a bounding rectangle around the waypoint, and
						// see if the mouse click occured
						// within its boundaries.
						Rectangle rect = new Rectangle(x - 5, y - ICON_SIZE,
								ICON_SIZE, ICON_SIZE);
						Point pt = event.getPoint();
						if (rect.contains(pt)) {
							log.debug("waypoint clicked {}", waypoint);
							event.consume();
							waypointUpdateListener.prepareEdit(waypoint);
							return;
						}
					}
				}
			}

			public void mouseClicked(MouseEvent event) {

				if (event.getClickCount() == 2 && !event.isConsumed()) {
					event.consume();
					if(waypointCreateListener.allowManipulate()) {
						Waypoint wp = new Waypoint(getGeoPositionForClick(map,
								event.getX(), event.getY()));
						waypoints.add(wp);
						waypointCreateListener.prepareCreate(wp);
						refreshPointsAndRepaint();	
					}

				} else {
					wayPointClicked(event);
				}
			}
		});
	}

	public void addPoi(Poi poi) {
		GeoPosition position = new GeoPosition(poi.getLat(), poi.getLng());
		waypoints.add(new PoiEnabledWaypoint(poi, position));

	}

	public void refreshPointsAndRepaint() {
		painter.setWaypoints(waypoints);
		map.repaint();
	}
	
	public void removeWaypoint(Waypoint wp) {
		waypoints.remove(wp);
		refreshPointsAndRepaint();
	}

	private GeoPosition getGeoPositionForClick(JXMapViewer map, double eventX,
			double eventY) {

		TileFactoryInfo info = map.getTileFactory().getInfo();
		Point2D centerPoint = GeoUtil.getBitmapCoordinate(map
				.getCenterPosition(), map.getZoom(), info);

		int w2 = map.getWidth() / 2;
		int h2 = map.getHeight() / 2;

		Point2D xy00 = new Point2D.Double(centerPoint.getX() - w2, centerPoint
				.getY()
				- h2);

		Point2D clickPoint = new Point2D.Double(xy00.getX() + eventX, xy00
				.getY()
				+ eventY);

		GeoPosition clickPosition = GeoUtil.getPosition(clickPoint, map
				.getZoom(), info);

		return clickPosition;
	}
}
